﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MusicPage.cs" company="3po.ru">
//   Author: Alexander Ulitin [support@3po.ru]
//   //   This file is part of Conusic.
//   //   Conusic is free software: you can redistribute it and/or modify
//   //   it under the terms of the GNU General Public License as published by
//   //   the Free Software Foundation, either version 3 of the License, or
//   //   (at your option) any later version.
//   //   Conusic is distributed in the hope that it will be useful,
//   //   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   //   GNU General Public License for more details.
//   //   You should have received a copy of the GNU General Public License
//   //   along with Conusic.  If not, see http://www.gnu.org/licenses/.
// </copyright>
// <summary>
//   The music page.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System.Text;

namespace MusicPage
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Drawing;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Windows.Forms;

    using Conusic;
    using Conusic.Properties;

    using ConusicUI;

    /// <summary>
    /// The music page.
    /// </summary>
    public partial class MusicPage : UserControl, IAudioSearchCommand
    {
        #region Constants

        /// <summary>
        /// The bitrate view index.
        /// </summary>
        private const int BitrateViewIndex = 4;

        /// <summary>
        /// The downloaded kb index.
        /// </summary>
        private const int DownloadedKbIndex = 3;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MusicPage"/> class.
        /// </summary>
        public MusicPage()
        {
            this.InitializeComponent();

            this.textBoxDownloadDirectory.Text =
                Environment.ExpandEnvironmentVariables(Settings.Default.DownloadDirectory);

            // textBoxDownloadDirectory.Text = Config.Instance.GetStringParam("DownloadDirectory");
        }

        #endregion

        #region Delegates

        /// <summary>
        /// The update list delegate.
        /// </summary>
        /// <param name="item">
        /// The item.
        /// </param>
        private delegate void UpdateListDelegate(ListViewItem item);

        #endregion

        #region Explicit Interface Methods

        /// <summary>
        /// The execute.
        /// </summary>
        /// <param name="listAudio">
        /// The list audio.
        /// </param>
        void IAudioSearchCommand.Execute(List<AudioRecord> listAudio)
        {
            this.SafeUpdateListView(listAudio);
        }

        #endregion

        #region Methods

        /// <summary>
        /// The get stream.
        /// </summary>
        /// <param name="stream">
        /// The stream.
        /// </param>
        /// <param name="download">
        /// The download.
        /// </param>
        private static void GetStream(Stream stream, DownloadFile download)
        {
            download.Download();
            download.OnCompleteResetEvent.WaitOne();
            using (var fstream = new FileStream(download.SaveToFile, FileMode.Open, FileAccess.Read))
            {
                var buffer = new byte[0x10000];
                int n;
                while ((n = fstream.Read(buffer, 0, buffer.Length)) != 0)
                {
                    stream.Write(buffer, 0, n);
                }
            }

            // stream.WriteByte(49);
        }

        private void SearchButtonClick(object sender, EventArgs e)
        {
            this.listView1.Items.Clear();
            VkontakteAudio.SearchAudioAsync(this, this.textBoxSearchText.Text);
        }

        private void Button2Click(object sender, EventArgs e)
        {
            var browserDialog = new FolderBrowserDialog
                {
                    Description = @"Пожалуйста, выберите папку для сохранения файлов",
                    SelectedPath = this.textBoxDownloadDirectory.Text
                };
            if (browserDialog.ShowDialog() == DialogResult.OK)
            {
                this.textBoxDownloadDirectory.Text = browserDialog.SelectedPath;
            }
        }

        /// <summary>
        /// The context menu track opening.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void ContextMenuTrackOpening(object sender, CancelEventArgs e)
        {
            int count = this.listView1.SelectedItems.Count;
            string returnRightForm = RussianNumbers.ReturnRightForm(count, "композицию", "композиции", "композиций");
            string returnRightForm2 = RussianNumbers.ReturnRightForm(count, "композиции", "композиций", "композиций");

            this.загрузитьToolStripMenuItem.Text = string.Format("Загрузить {0} {1}", count, returnRightForm);
            this.получитьРазмерИБитрейтToolStripMenuItem.Text = string.Format(
                "Получить битрейт для {0} {1}", count, returnRightForm2);

            bool anydownloaded = false;
            foreach (var record in SelectedAudioRecords())
            {
                anydownloaded |= File.Exists(Path.Combine(Settings.Default.DownloadDirectory, record.Filename));
            }
            openFileToolStripMenuItem.Enabled = anydownloaded;
        }

        /// <summary>
        /// The download tool strip menu item click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void DownloadToolStripMenuItemClick(object sender, EventArgs e)
        {
            this.StartLoadSelectedItems();
        }

        /// <summary>
        /// The generate virtual file list.
        /// </summary>
        /// <param name="selectedListViewItemCollection">
        /// The selected list view item collection.
        /// </param>
        /// <returns>
        /// The <see cref="VirtualFileDataObject"/>.
        /// </returns>
        private VirtualFileDataObject GenerateVirtualFileList(IEnumerable selectedListViewItemCollection)
        {
            var virtualFile = new VirtualFileDataObject();
            var list = new List<VirtualFileDataObject.FileDescriptor>();
            foreach (ListViewItem item in selectedListViewItemCollection)
            {
                AudioRecord record = item.Tag as AudioRecord;
                var download = this.GetDownloadFile(record, item, Path.GetRandomFileName());
                list.Add(
                    new VirtualFileDataObject.FileDescriptor()
                        {
                            Name = record.Filename,
                            ChangeTimeUtc = DateTime.Now,
                            StreamContents = stream => GetStream(stream, download)
                        });
            }

            virtualFile.SetData(list);

            return virtualFile;
        }

        /// <summary>
        /// The get download file.
        /// </summary>
        /// <param name="record">
        /// The record.
        /// </param>
        /// <param name="item">
        /// The item.
        /// </param>
        /// <param name="saveFilename">
        /// The save filename.
        /// </param>
        /// <returns>
        /// The <see cref="DownloadFile"/>.
        /// </returns>
        private DownloadFile GetDownloadFile(AudioRecord record, ListViewItem item, string saveFilename)
        {
            if (record == null)
            {
                throw new ArgumentNullException("record");
            }

            if (!string.IsNullOrEmpty(record.Url))
            {
                var downloadFile = new DownloadFile(record.Url, saveFilename);
                downloadFile.OnFileComplete += (fileEventArgs) =>
                    {
                        this.UpdateItemListViewText(item, "загружено", DownloadedKbIndex);
                        item.BackColor = Color.LightGreen;
                    };

                downloadFile.OnFileFail += (fileEventArgs) =>
                    {
                        ErrorReport.Show(fileEventArgs.Exc, null);
                        this.UpdateItemListViewText(item, "Ошибка! ", DownloadedKbIndex);
                    };
                DateTime lastUpdateTime = DateTime.MinValue;

                // timeSpan is not Value type - we can use it in new delegate
                var timeSpan = new TimeSpan(0, 0, 0, 0, 500);
                downloadFile.OnProgressFile += (args) =>
                    {
                        if (DateTime.Now - lastUpdateTime < timeSpan)
                        {
                            return;
                        }

                        lastUpdateTime = DateTime.Now;
                        string a = string.Format(
                            "{2}% {0} Kb/{1}Kb",
                            args.DownloadedKb,
                            args.TotalKb,
                            (int)args.Downloaded * 100 / args.Total);

                        this.UpdateItemListViewText(item, a, DownloadedKbIndex);
                        string s = string.Format(
                            "{0:D3} bps", Convert.ToInt32(args.Total * 8 / 1024.0 / record.Duration));
                        this.UpdateItemListViewText(item, s, BitrateViewIndex);
                    };

                return downloadFile;
            }
            else
            {
                AudioRecord foundedRecord = null;
                try
                {
                    foundedRecord = VkontakteAudio.SearchOneAudio(record.ArtistAndComposition);
                }
                catch { }

                if (foundedRecord == null || foundedRecord.Url == null)
                {
                    this.UpdateItemListViewText(item, "Не найдено! ", DownloadedKbIndex);
                    return null;
                }
                else
                {
                    return this.GetDownloadFile(foundedRecord, item, saveFilename);
                }

            }
        }

        /// <summary>
        /// The get size and bitrate tool strip menu item click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void GetSizeAndBitrateToolStripMenuItemClick(object sender, EventArgs e)
        {
            foreach (ListViewItem item in this.listView1.SelectedItems)
            {
                ListViewItem item1 = item;
                var s = new ThreadStart(
                    () =>
                    {
                        if (item1 != null && item1.Tag as AudioRecord != null)
                        {
                            var averageBitrate = VkontakteAudio.GetAverageBitrate(item1.Tag as AudioRecord);
                            string text = string.Format("{0:D3} kbps", averageBitrate);
                            this.UpdateItemListViewText(item1, text, BitrateViewIndex);
                        }
                    });
                var thread = new Thread(s);
                thread.Start();
            }
        }

        /// <summary>
        /// The list view 1 column click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void ListView1ColumnClick(object sender, ColumnClickEventArgs e)
        {
            var comparer = this.listView1.ListViewItemSorter as MusicPageViewSorter;
            if (comparer == null)
            {
                comparer = new MusicPageViewSorter();
                this.listView1.ListViewItemSorter = comparer;
            }

            comparer.SetColumnSorting(e.Column);
            this.listView1.Sort();
        }

        /// <summary>
        /// The safe update list view.
        /// </summary>
        /// <param name="list">
        /// The list.
        /// </param>
        private void SafeUpdateListView(IEnumerable<AudioRecord> list)
        {
            if (list == null)
            {
                return;
            }

            if (this.listView1.InvokeRequired)
            {
                this.listView1.Invoke(new MethodInvoker(() => this.SafeUpdateListView(list)));
            }
            else
            {
                this.listView1.Items.Clear();
                foreach (AudioRecord item in list)
                {
                    var newItem = new ListViewItem { Text = item.Artist };
                    newItem.SubItems.Add(item.CompositionName);
                    newItem.SubItems.Add(item.DurationMs);
                    newItem.SubItems.Add(string.Empty); // size
                    newItem.SubItems.Add(string.Empty); // bitrate

                    newItem.Tag = item;
                    this.listView1.Items.Add(newItem);
                }
            }
        }

        /// <summary>
        /// The start load selected items.
        /// </summary>
        private void StartLoadSelectedItems()
        {
            if (!this.EnsureDownloadDirExists()) return;

            foreach (ListViewItem item in this.listView1.SelectedItems)
            {
                var record = item.Tag as AudioRecord;
                Debug.Assert(record != null, "record != null");
                var downloadFile = this.GetDownloadFile(record, item, string.Format(record.GetFullFilename()));
                if(downloadFile!=null)
                {
                    downloadFile.DownloadAsync();
                }
            }
        }

        private bool EnsureDownloadDirExists()
        {
            var dirExists = Directory.Exists(Settings.Default.DownloadDirectory);

            if (!dirExists)
            {
                var result = MessageBox.Show(
                    this,
                    string.Format("Директории {0} не существует? Желаете создать ее?", Settings.Default.DownloadDirectory),
                    "Conusic",
                    MessageBoxButtons.OKCancel,
                    MessageBoxIcon.Question);
                if (result == DialogResult.OK)
                {
                    Directory.CreateDirectory(Settings.Default.DownloadDirectory);
                }
                else
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// The text box download directory text changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void TextBoxDownloadDirectoryTextChanged(object sender, EventArgs e)
        {
            Settings.Default.DownloadDirectory = this.textBoxDownloadDirectory.Text;
        }

        /// <summary>
        /// The text box search text key up.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void TextBoxSearchTextKeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                this.SearchButtonClick(sender, null);
            }
        }

        /// <summary>
        /// The top 100 tool strip menu item_ click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void Top100ToolStripMenuItemClick(object sender, EventArgs e)
        {
            var ts = new ThreadStart(
                delegate()
                {
                    try
                    {
                        var list = PiterMoskvaFm.GetTop100PiterFm();
                        this.SafeUpdateListView(list);
                    }
                    catch (Exception ext)
                    {
                        ErrorReport.Show(ext);
                    }
                });
            var th = new Thread(ts);
            th.Start();
        }

        private void openFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var list = SelectedAudioRecords();
            Process.Start(Path.Combine(Settings.Default.DownloadDirectory, list[0].Filename));
        }

        private List<AudioRecord> SelectedAudioRecords()
        {
            var list = new List<AudioRecord>();
            foreach (ListViewItem item in this.listView1.SelectedItems)
            {
                var record = item.Tag as AudioRecord;
                Debug.Assert(record != null, "record != null");
                list.Add(record);
            }
            return list;
        }

        /// <summary>
        /// The update item list view text.
        /// </summary>
        /// <param name="item">
        /// The item.
        /// </param>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="subitemIndex">
        /// The subitem index.
        /// </param>
        private void UpdateItemListViewText(ListViewItem item, string text, int subitemIndex)
        {
            if (this.listView1.InvokeRequired)
            {
                this.listView1.Invoke(new MethodInvoker(() => this.UpdateItemListViewText(item, text, subitemIndex)));
            }
            else
            {
                item.SubItems[subitemIndex].Text = text;
            }
        }

        /// <summary>
        /// The vkontakte button 1 click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void VkontakteButton1Click(object sender, EventArgs e)
        {
            this.StartLoadSelectedItems();
        }

        private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            this.StartLoadSelectedItems();
        }

        /// <summary>
        /// The list view 1_ mouse down.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void listView1_MouseDown(object sender, MouseEventArgs e)
        {
            return;

            IEnumerable items = new List<ListViewItem>();
            var pointToClient = e.Location;
            var activeItem = this.listView1.GetItemAt(pointToClient.X, pointToClient.Y);
            if (activeItem != null && activeItem.Selected)
            {
                items = this.listView1.SelectedItems;
            }
            else if (activeItem != null)
            {
                if ((Control.ModifierKeys & (Keys.Shift | Keys.Alt | Keys.Control)) != Keys.None)
                {
                    return;
                }
                else
                {
                    listView1.SelectedItems.Clear();
                    activeItem.Selected = true;
                    items = new[] { activeItem };
                }
            }
            else
            {
                return;
            }

            var virtualFileDataObject = this.GenerateVirtualFileList(items);
            try
            {
                VirtualFileDataObject.DoDragDrop(virtualFileDataObject, DragDropEffects.Copy);
            }
            catch (COMException)
            {
                // Failure; no way to recover
            }
        }

        #endregion

        private void ImportListToolStripMenuItemClick(object sender, EventArgs e)
        {
            this.listView1.Items.Clear();
            var dialog = new ImportSongDialog();
            if(dialog.ShowDialog(this) != DialogResult.OK)
                return;
            
            foreach (string item in dialog.Songs)
            {
                var newItem = new ListViewItem { Text = item };
                newItem.SubItems.Add(string.Empty);
                newItem.SubItems.Add(string.Empty);
                newItem.SubItems.Add(string.Empty); // size
                newItem.SubItems.Add(string.Empty); // bitrate

                newItem.Tag = new AudioRecord(item, string.Empty);
                this.listView1.Items.Add(newItem);
            }
        }
    }
}